package org.custom.spring.boot.security.config;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

/**
 * 系统相关配置
 *
 * @author LiKun
 */
@Configuration
@MapperScan("org.custom.spring.boot.*.mapper")
@ComponentScan({"com.knife", "org.cleaver.spring"})
public class SystemConfiguration implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    private static final String HOST = "localhost";

    @Value("${server.port:8080}")
    private int port;

    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    @Override
    public void customize(TomcatServletWebServerFactory servletWebServerFactory) {
        while (isPortInUse()) {
            port++;
        }
        // 设置端口号
        servletWebServerFactory.setPort(port);
    }

    /**
     * 请求路劲匹配器
     *
     * @return 匹配器
     */
    @Bean
    public PathMatcher pathMatcher() {
        return new AntPathMatcher();
    }

    /**
     * redis相关序列化工具类
     *
     * @return 序列化
     */
    @Bean
    public StringRedisSerializer stringRedisSerializer() {
        return new StringRedisSerializer();
    }

    /**
     * redis相关序列化工具类
     *
     * @return 序列化类
     */
    @Bean
    public GenericFastJsonRedisSerializer genericFastJsonRedisSerializer() {
        return new GenericFastJsonRedisSerializer();
    }

    /**
     * 注册并配置 ObjectMapper
     *
     * @return ObjectMapper
     */
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 属性值为 null 的字段不会被序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // ObjectMapper 配置：序列化时忽略未知字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        return objectMapper;
    }

    /**
     * RedisTemplate 操作类相关配置
     *
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate redisTemplate(StringRedisSerializer stringRedisSerializer,
                                       GenericFastJsonRedisSerializer genericFastJsonRedisSerializer) {
        RedisTemplate<String, Object> template = new RedisTemplate();

        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setValueSerializer(genericFastJsonRedisSerializer);
        template.setHashValueSerializer(genericFastJsonRedisSerializer);
        template.setConnectionFactory(redisConnectionFactory);

        return template;
    }

    /**
     * 解决前后端分离项目的跨域问题
     *
     * @return CorsWebFilter
     */
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();

        //允许该IP的客户端访问
        corsConfiguration.addAllowedOrigin("*");
        //允许客户端携带的请求头
        corsConfiguration.addAllowedHeader("*");
        //允许客户端访问的请求方式
        corsConfiguration.addAllowedMethod("*");
        //是否允许请求带有验证信息
        corsConfiguration.setAllowCredentials(true);
        //暴露的头部信息(可以添加多个)
        corsConfiguration.addExposedHeader("Authorization");
        // 配置该CORS配置源适应的路径
        configurationSource.registerCorsConfiguration("/**", corsConfiguration);

        return new CorsWebFilter(configurationSource);
    }


    /*--------------------------------------------------private methods-----------------------------------------------*/


    /**
     * 判断本机端口号是否被占用
     *
     * @return 判断结果
     */
    private boolean isPortInUse() {
        boolean isPortInUse = true;
        try {
            InetAddress theAddress = InetAddress.getByName(HOST);
            Socket socket = new Socket(theAddress, port);
        } catch (IOException exception) {
            isPortInUse = false;
        }
        return isPortInUse;
    }

}
